• 问题

    不可变类是其实例不能被修改的类,没有实例中所包含的数据域,在实例被创建的时候被初始化,且在实例的生命周期中不能被修改。JAVA中有许多不可变类,如String,值的基本包装类型,BigInteger和BigDecimal等,不可变类是线程安全的。不可变有很多优点,那么设计不可变类的原则有哪些?

  • 解决

    1. 设计不可变类有以下几条规则:

      • 不要提供任何修改实例数据域的setter方法
      • 保证类不会被扩展:防止子类恶意修改实例对象,应该禁止类被子类扩展,可以将其定义为final;或者让类所有的构造器都变成私有的或者包级私有的,并添加公有的静态工厂来代替公有的构造器;
      • 所有的域都是final的;
      • 所有的域都成为私有的,这样可以防止客户端获得访问被域引用的可变对象的权限,并防止客户端修改这些对象;
      • 确保对于任何可变组件的互斥性访问:如果类具有指向可变对象的域,则必须确保客户端无法获得指向这些对象的引用;
    2. 示例

      例如,String不可变类的具体实现为:

      public final class String
          implements java.io.Serializable, Comparable<String>, CharSequence
      {
          /** The value is used for character storage. */
          private final char value[];
          /** The offset is the first index of the storage that is used. */
          private final int offset;
          /** The count is the number of characters in the String. */
          private final int count;
          /** Cache the hash code for the string */
          private int hash; // Default to 0
          ....
          public String(char value[]) {
              this.value = Arrays.copyOf(value, value.length); 
           }
          ...
           public char[] toCharArray() {
           // Cannot use Arrays.copyOf because of class initialization order issues
              char result[] = new char[value.length];
              System.arraycopy(value, 0, result, 0, value.length);
              return result;
          }
          ...
      }
      

      如上代码所示,可以观察到以下设计细节:

      1. String类被final修饰,不可继承
      2. string内部所有成员都设置为私有变量
      3. 不存在value的setter
      4. 并将value和offset设置为final。
      5. 当传入可变数组value[]时,进行深拷贝而不是直接将value[]复制给内部变量.
      6. 获取value时不是直接返回对象引用,而是返回对象的copy.
  • 结论

    不可变类有很多好处,因此合适的适用场景下,可以考虑将类设计生不可变类,并遵守不可变类的设计原则。

results matching ""

    No results matching ""